package com.onpositive.knowledge.model.value.audits;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.onpositive.data.IPropertyConstants;
import com.onpositive.semantic.model.api.meta.IMeta;
import com.onpositive.semantic.model.api.meta.IWritableMeta;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.units.ParsedIdent;
import com.onpositive.units.ParsedString;
import com.onpositive.units.ParsedValueSequence;

public class IdentAudit extends AuditSourceContainer  implements IValueAudit
{

	/**
	 * 
	 */
	private static final long serialVersionUID = -7057523522452968590L;
	public final static String ID = "ident-audit" ;

	@Override
	public void perform(IProperty prop, Iterable<Object> objects, AbstractAuditResult auditResult)
	{
		if( auditResult == null || !(auditResult instanceof OwnAuditResult ) )
			return ;

		String name = prop.getId() ;
		ValueCounterAudit statsAudit = new ValueCounterAudit();
		
		ValueCounterAuditResult sar = new ValueCounterAuditResult(prop) ;
		Map<String,Object> metaInfo = new HashMap<String, Object>() ;
		metaInfo.put("audit-result", sar) ;
		statsAudit.iterateObjects(prop, objects, metaInfo, 0) ;

		final HashMap<Object, ? extends Object> comparisonMap = sar.getStatsMap();
		
		Comparator<Object> customComp = new Comparator<Object>() {
			@Override
			public int compare(Object o1, Object o2) {
				return doCompare(comparisonMap.get(o1), comparisonMap.get(o2));
			}
		};

		ArrayList<Object> lst = new ArrayList<Object>(comparisonMap.keySet());
		Collections.sort(lst, customComp);
		
		
		int valuesCount = comparisonMap.size() ;
		int objectsConut = 0 ;
		for( Map.Entry<Object, ? extends Object> entry : comparisonMap.entrySet() )
		{
			Object val = entry.getValue() ;
			if( val == null || !(val instanceof Integer) )
				continue ;
			
			objectsConut += (Integer) val ;			
		}
		
		double ratio = ((double)objectsConut)/valuesCount ;
		if( ratio < 3 )
			return ;
		
		System.out.print( name ) ;
		System.out.print(", rati0 = ") ;
		System.out.print(ratio) ;
		System.out.print("\n") ;
		for( int i = 0 ; i < Math.min(10, lst.size()); i++ )
		{
			System.out.print( lst.get(i).toString() ) ;
			System.out.print( "   " ) ;
			System.out.print( comparisonMap.get(lst.get(i)).toString() ) ;
			System.out.print( "\n" ) ;
		}
		System.out.print( "\n" ) ;
		
		IMeta meta = prop.getMeta().getParentMeta() ;
		IWritableMeta wMeta = (IWritableMeta) meta ;
		wMeta.putMeta( IPropertyConstants.CHAR_IS_IDENTIFICATOR, true ) ;
		
	}
	
	
	
	protected static int doCompare(Object arg1, Object arg2) {
		if (!((arg1 instanceof Integer) && (arg2 instanceof Integer)) || arg1 == null || arg2 == null ) {
			//TODO
			// throw horrible error
			return Integer.MAX_VALUE;
		}

		Integer i1 = (Integer) arg1;
		Integer i2 = (Integer) arg2;

		return i2 - i1;
	}

	private final static HashSet<String> acceptedValueTypes = new HashSet<String>( 
			Arrays.asList(new String[]{ ParsedString.ID, ParsedIdent.ID, ParsedValueSequence.ID }) ) ;
	
	@Override
	public AbstractAuditResult createAuditResult(IProperty prop)
	{		
		String name = prop.getMeta().getSingleValue( "id", String.class, null ) ;
		IMeta meta = prop.getMeta() ;
		if( !(meta instanceof IWritableMeta) )
			return null;
		
		Boolean isService = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_IS_SERVICE_PROPERTY, Boolean.class, null) ;
		if( isService != null && isService )
			return null;
		
		Boolean isDepricated = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_DEPRICATED_LABEL, Boolean.class, null);
		if( isDepricated != null && isDepricated )
			return null;
		
		String parserId = prop.getMeta().getSingleValue( IPropertyConstants.CHAR_PARSER_ID_LABEL, String.class, null);
		if( !acceptedValueTypes.contains( parserId ) )
			return null ;
		
		AbstractAuditResult abstractAuditResult = new OwnAuditResult( prop );
		abstractAuditResult.setLooksApplyable(true) ;
		abstractAuditResult.setAuditId( ID ) ;
		return abstractAuditResult;
	}
	
	public static class OwnAuditResult extends AbstractAuditResult{

		/**
		 * 
		 */
		private static final long serialVersionUID = 5609838439607786495L;

		public OwnAuditResult() {
			super();
		}

		public OwnAuditResult(IProperty prop) {
			super(prop);
		}
				
	}

	@Override
	public Set<String> getRequiredAudits() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getId() {
		return ID;
	}
}
